#include "sys_uart.h"
#include "sys_clock.h"
#include "sys_gpio.h"
#include "sys_delay.h"
#include "sys_lcd.h"
#include <string.h>
#include <stdio.h>
#include "sys_spi.h"
#include "sys_sd.h"
#include "sys_mmu.h"
//-----------------------
#include "sys_lcd_conf.h"
//-----------------------

#include "i2c.h"
#include "hdmi_tx.h"

#include "tfs.h"

#define LOGO_OFFSET   0x00016000
#define KERNEL_OFFSET 0x00050000
#define VRAM_BASE     0x81800000
#define KERNEL_BASE   0x81000000

#define ENV_OFFSET    0x80010000

#define EXT_APP_NAME  "startup.bin"

#ifdef EVB_IO
#define DEBUG_UART_PORT UART0
#else
#define DEBUG_UART_PORT UART2
#endif

//#define ENABLE_USER_KEY

typedef struct app_env_t {
	unsigned int card_ok;
	unsigned int card_type;
	unsigned int card_uRCA;
} APP_ENV;

// in sys_sd.c
int card_ok = 0;
extern unsigned int CARD_TYPE;
extern unsigned int CARD_uRCA;

APP_ENV* the_app_env;

/*---------------------------------------------------
在sys_lcd_conf.h中配置LCD信息
----------------------------------------------------*/

int cmd_loop();

//__align(4) unsigned short *BT;

/*---------------------------------------------------
---- main
---- main
----------------------------------------------------*/

void show_bar()
{
	int x, y;

	for (x = 0; x < 1280; x++) {
		int gray = x / 5;
		unsigned short cr;

		cr = (gray >> 3) & 0x1F;
		for (y = 0; y < 240; y++) {
			*((short*)VRAM_BASE + y * XSIZE_PHYS + x) = cr;
		}

		cr = ((gray >> 2) & 0x3F) << 5;
		for (; y < 480; y++) {
			*((short*)VRAM_BASE + y * XSIZE_PHYS + x) = cr;
		}

		cr = ((gray >> 3) & 0x1F) << 11;
		for (; y < 720; y++) {
			*((short*)VRAM_BASE + y * XSIZE_PHYS + x) = cr;
		}
	}
}

void show_logo()
{
	int logo_addr;
	short logo_header[4];

	int width;
	int height;
	short* dst;

	sys_spi_flash_read(SPI0, LOGO_OFFSET, logo_header, 8);

	if (logo_header[0] != 0x4F4C || logo_header[1] != 0x4F47) {// 'LOGO'
		return;
	}

	width = logo_header[2];
	height = logo_header[3];

	sysprintf("logo found [%d x %d].\r\n", width, height);

	logo_addr = LOGO_OFFSET + 8;

	// center
	dst = (short*)VRAM_BASE + ((YSIZE_PHYS - height) / 2) * XSIZE_PHYS + (XSIZE_PHYS - width) / 2;

	while (height--) {
//		int w;
//		short* p;

		sys_spi_flash_read(SPI0, logo_addr, dst, width * 2);

//		w = width;
//		p = dst;
//		while (w--) {
			//*p++ = 0xFFFF;
//		}

		logo_addr += width * 2;
		dst += XSIZE_PHYS;
	}
}

static const char BOOTEXE[8] = "eGON.EXE";

int load_from_external(void)
{
	int i;
	char* file_h = (char*)KERNEL_BASE;
	unsigned int file_size;

	if (SD_Init(0)) {
		sysprintf("SD init failed.\r\n");
		return -1;
	}

	the_app_env->card_ok   = 1;
	the_app_env->card_type = CARD_TYPE;
	the_app_env->card_uRCA = CARD_uRCA;

	if (tfs_init()) {
		sysprintf("TFS init failed.\r\n");
		return -2;
	}

	sysprintf("\r\nRead " EXT_APP_NAME " ...\r\n");

	if (tfs_load(file_h, EXT_APP_NAME, 8)) {
		sysprintf("Read header failed.\r\n");
		return -2;
	}

	for (i = 0; i < 8; i++) {
		if (file_h[4 + i] != BOOTEXE[i]) {
			sysprintf("Header not match.\r\n");
			return -3;
		}
	}

	//读文件大小
	file_size = file_h[0x13] << 24 | file_h[0x12] << 16 | file_h[0x11] << 8 | file_h[0x10];

	sysprintf("File Size %d\r\n", file_size);

	//读文件
	sysprintf("Read exec to RAM 0x%08x \r\n", KERNEL_BASE);
	tfs_load((void*)(KERNEL_BASE - 32), EXT_APP_NAME, 0x800000);

	return 0;
}

int load_from_internal(void)
{
	int i;
	char file_h[32];
	unsigned int file_size;

	sysprintf("\r\nLoad internal...\r\n");

	sys_spi_flash_read(SPI0, KERNEL_OFFSET, file_h, 32);//读文件头

	for (i = 0; i < 8; i++) {
		if (file_h[4 + i] != BOOTEXE[i]) {
			sysprintf("Header not match.\r\n");
			return -1;
		}
	}

	sysprintf("\r\nRead EXEC...\r\n");

	//读文件大小
	file_size = file_h[0x13] << 24 | file_h[0x12] << 16 | file_h[0x11] << 8 | file_h[0x10];
	file_size = file_size - 32;

	sysprintf("Exec at Flash 0x%08x\r\n", KERNEL_OFFSET);
	sysprintf("File Size %d \r\n", file_size);

	//读文件
	sysprintf("Read exec to RAM 0x%08x \r\n", KERNEL_BASE);
	sys_spi_flash_read(SPI0, KERNEL_OFFSET + 32, (unsigned int*)(KERNEL_BASE), file_size);

	return 0;
}

int main(void)
{
	int i;

	Sys_SET_UART_DBG(DEBUG_UART_PORT);

	sysprintf("\r\n\r\n*****************************************\r\n");
	sysprintf("F1Cx00S BOOT1 START...\r\n");
	sysprintf("[" __DATE__ "][" __TIME__ "]...\r\n");
	sysprintf("*****************************************\r\n");

	sys_mmu_init();

	//读文件头
//	sys_spi_flash_read(SPI0,spireadAddr,file_h,32);

	//初始化LCD
#if 0
	// ARGB 8888
	for (i = 0; i < XSIZE_PHYS * YSIZE_PHYS; i++) {
		*(unsigned int*)(VRAM_BASE + i * 4) = 0;
	}
#else
	// RGB565
	for (i = 0; i < XSIZE_PHYS * YSIZE_PHYS; i++) {
		*(unsigned short*)(VRAM_BASE + i * 2) = 0;
	}
#endif

	i2c_init();
	Sys_LCD_Init(XSIZE_PHYS, YSIZE_PHYS, (unsigned int*)VRAM_BASE, NULL);
	hdmi_init();
	sysprintf("Show logo...\r\n");

//	show_bar();
	show_logo();

#ifdef ENABLE_USER_KEY
	// user key
	GPIO_Congif(GPIOA, 0, GPIO_Mode_IN, GPIO_PuPd_UP);
	GPIO_Congif(GPIOE, 12, GPIO_Mode_IN, GPIO_PuPd_UP);

	for (;;) {
//		GPIO_RESET(GPIOE, 2);
//		delay_ms(500);
//		GPIO_SET(GPIOE, 2);
//		delay_ms(500);
		if (GPIO_READ(GPIOA, 0))  GPIO_SET(GPIOA, 1); else GPIO_RESET(GPIOA, 1);
		if (GPIO_READ(GPIOE, 12)) GPIO_SET(GPIOE, 2); else GPIO_RESET(GPIOE, 2);
	}

	if (0 == GPIO_READ(GPIOA, 0)) {
		// user key pressed
		cmd_loop();
	}
#endif

	// test CH9350
	if (0) {

		Sys_Uart_Init(UART1, get_cpu_frequency(), 115200, 0);

		for (;;) {
			int ch;

			ch = Sys_Uart_Get(UART1);
			sysprintf("%02X ", ch);
			
		}
	}

	the_app_env = (APP_ENV*)ENV_OFFSET;

	the_app_env->card_ok = 0;

	if (load_from_external()) {
		if (load_from_internal()) {

			sysprintf("Failed, start console...\r\n");
			cmd_loop();
			while(1) {
				int ch = Sys_Uart_Getc();
				Sys_Uart_Putc(ch);
			}
		}
	}

	//执行
	sysprintf("Jump Exec. 0x%08X(0x%08X)...\r\n", KERNEL_BASE, the_app_env);
	((void(*)(APP_ENV*))KERNEL_BASE)(the_app_env);
}

#if 1
char *uart_gets(char *p,int s)
{
	char *ret = p;
	int c;

	while(--s)
	{
	c=Sys_Uart_Getc();

	if(c=='\n'||c=='\r') break;
	Sys_Uart_Putc(c);
	if(c=='\b') // backspace!
	{
		if(p!=ret) 
		{
			*--p = 0;
			s++;
		}
	}
	else
		*p++ = c;
	}
	Sys_Uart_Putc('\r');
	Sys_Uart_Putc('\n');
	*p=0;

	return p==ret ? 0 : ret;
}

int xtoi(char *s1)
{
    int ret;
    
    for(ret=0;s1&&*s1;s1++) 
    {
        if(*s1<='9')
            ret = *s1-'0'+(ret<<4); // val = val*16+int(*s1)
        else
            ret = 10+(*s1&0x5f)-'A'+(ret<<4); // val = val*16+int(toupper(*s1))
    }
    
    return ret;
}

int strncmp(const char *s1,const char *s2,size_t len)
{
    while(--len && *s1 && *s2 && (*s1==*s2)) s1++, s2++;
    
    return (*s1-*s2);
}

int strcmp(const char *s1, const char *s2)
{
    return strncmp(s1,s2,-1);
}

size_t strlen(const char *s1)
{
    int len;
    
    for(len=0;s1&&*s1++;len++);

    return len;
}

char *strtok(char *str, const char *dptr)
{
    static char *nxt = 0;

    int dlen = strlen(dptr);
    char *tmp;

         if(str) tmp=str;
    else if(nxt) tmp=nxt;
    else return 0;
    
    char *ret=tmp;

    while(*tmp)
    {
        if(strncmp(tmp,dptr,dlen)==0)
        {
            *tmp=0;
            nxt = tmp+1;
            return ret;
        }
        tmp++;
    }
    nxt = 0;
    return ret;
}

void uart_puts(const char* s)
{
	sysPutString(s);
	Sys_Uart_Putc('\r');
	Sys_Uart_Putc('\n');
}

void wait_user_key()
{
	// wait release
	for (;;) {
		if (1 == GPIO_READ(GPIOA, 0)) {
			delay_ms(10);
			if (1 == GPIO_READ(GPIOA, 0))
				break;
		}
	}

	// wait press
	for (;;) {
		if (0 == GPIO_READ(GPIOA, 0)) {
			delay_ms(10);
			if (0 == GPIO_READ(GPIOA, 0))
				break;
		}
	}
}

int cmd_loop()
{
	char str_cmds[64];

	for (;;) {
		int argc;
		char* token;
		char* argv[16];

		Sys_Uart_Putc('>');
		str_cmds[0] = 0;
		uart_gets(str_cmds, 63);

		argc = 0;
		argv[0] = str_cmds;
		token = strtok(str_cmds, " ");
		while (token) {
			argv[argc++] = token;
			token = strtok(0, " ");
		}

		if (0 == strcmp(argv[0], "r")) {
			int addr;
			int data;

			if (argc < 2) {
				uart_puts("Address missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = *(volatile int*)addr;

			sysprintf("Read [%X] = %X\n", addr, data);
		} else if (0 == strcmp(argv[0], "exit")) {
			break;
		} else if (0 == strcmp(argv[0], "key")) {
			int index;
			int data;

			if (argc < 2) {
				uart_puts("Index missing.");
				continue;
			}

			index = xtoi(argv[1]);
			data = GPIO_READ(GPIOA, index);

			sysprintf("Read key [%d] = %X\n", index, data);
		} else if (0 == strcmp(argv[0], "rb")) {
			int addr;
			int data;

			if (argc < 2) {
				uart_puts("Address missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = *(volatile unsigned char*)addr;

			sysprintf("Read Byte [%X] = %X\n", addr, data);
		} else if (0 == strcmp(argv[0], "rh")) {
			int addr;
			int data;

			if (argc < 2) {
				uart_puts("Address missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = *(volatile unsigned short*)addr;

			sysprintf("Read Short [%X] = %X\n", addr, data);
		} else if (0 == strcmp(argv[0], "c")) {
			int ret;
			int addr;
			int data;

			if (argc < 3) {
				uart_puts("Address or data missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = xtoi(argv[2]);

			ret = ((int(*)(int))addr)(data);

			sysprintf("Call [%X](%X) = %X\n", addr, data, ret);
		} else if (0 == strcmp(argv[0], "w")) {
			int addr;
			int data;

			if (argc < 3) {
				uart_puts("Address or data missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = xtoi(argv[2]);

			*(volatile int*)addr = data;

			sysprintf("Write [%X] = %X\n", addr, data);
		} else if (0 == strcmp(argv[0], "wb")) {
			int addr;
			int data;

			if (argc < 3) {
				uart_puts("Address or data missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = xtoi(argv[2]);

			data &= 0xFF;

			addr |= 0x800000;
			*(volatile unsigned char*)addr = data;

			sysprintf("Write Byte [%X] = %X\n", addr, data);
		} else if (0 == strcmp(argv[0], "wh")) {
			int addr;
			int data;

			if (argc < 3) {
				uart_puts("Address or data missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = xtoi(argv[2]);

			data &= 0xFFFF;

			*(volatile unsigned short*)addr = data;

			sysprintf("Write Short [%X] = %X\n", addr, data);
		} else if (0 == strcmp(argv[0], "fill")) {
			int width;
			int addr;
			int data;
			int len;

			if (argc < 5) {
				uart_puts("usage fill <1|2|4> <addr> <data> <len>.");
				continue;
			}

			width = xtoi(argv[1]);
			addr  = xtoi(argv[2]);
			data  = xtoi(argv[3]);
			len   = xtoi(argv[4]);

			switch (width) {
				case 1:
					data &= 0xFF;
					while (len--) {
						*(volatile unsigned char*)addr = data;
						addr++;
					}
					break;
				case 2:
					data &= 0xFFFF;
					while (len--) {
						*(volatile unsigned short*)addr = data;
						addr += 2;
					}
					break;
				case 4:
					while (len--) {
						*(volatile unsigned int*)addr = data;
						addr += 2;
					}
					break;
				default:
					uart_puts("bad width.");
					break;
			}

		} else if (0 == strcmp(argv[0], "video-test")) {
			int x, y;
			int len;
			unsigned short* p;
			unsigned short* vbase = (unsigned short*)VRAM_BASE;

			// black
			p = vbase;
			len = XSIZE_PHYS * YSIZE_PHYS;
			while (len--) *p++ = 0;
			wait_user_key();

			// white
			p = vbase;
			len = XSIZE_PHYS * YSIZE_PHYS;
			while (len--) *p++ = 0xFFFF;
			wait_user_key();

			// red
			p = vbase;
			len = XSIZE_PHYS * YSIZE_PHYS;
			while (len--) *p++ = 0xF800;
			wait_user_key();

			// green
			p = vbase;
			len = XSIZE_PHYS * YSIZE_PHYS;
			while (len--) *p++ = 0x07E0;
			wait_user_key();

			// blue
			p = vbase;
			len = XSIZE_PHYS * YSIZE_PHYS;
			while (len--) *p++ = 0x001F;
			wait_user_key();

			// frame
			p = vbase;
			for (y = 0; y < YSIZE_PHYS; y++) {
				for (x = 0; x < XSIZE_PHYS; x++) {
					*p++ = ((x == 0) || (x == XSIZE_PHYS - 1) ||
						    (y == 0) || (y == YSIZE_PHYS - 1))
						? 0xFFFF : 0;
				}
			}
			wait_user_key();

			// H fade
			p = vbase;
			for (y = 0; y < YSIZE_PHYS; y++) {
				for (x = 0; x < XSIZE_PHYS; x++) {
					unsigned c = x * 256 / XSIZE_PHYS;
					unsigned r, g, b;

					r = (c >> 3) & 0x1F; // 5 bits for red
					g = (c >> 2) & 0x3E; // 6 bits for green
					b = (c >> 3) & 0x1F; // 5 bits for blue

					*p++ = (r << 11) | (g << 5) | b;
				}
			}
			wait_user_key();

			// V fade
			p = vbase;
			for (y = 0; y < YSIZE_PHYS; y++) {
				for (x = 0; x < XSIZE_PHYS; x++) {
					unsigned c = y * 256 / YSIZE_PHYS;
					unsigned r, g, b;

					r = (c >> 3) & 0x1F; // 5 bits for red
					g = (c >> 2) & 0x3E; // 6 bits for green
					b = (c >> 3) & 0x1F; // 5 bits for blue

					*p++ = (r << 11) | (g << 5) | b;
				}
			}
		} else if (0 == strcmp(argv[0], "p")) {
#if 0
			int addr;
			int data;

			if (argc < 3) {
				uart_puts("Address or data missing.");
				continue;
			}

			addr = xtoi(argv[1]);
			data = xtoi(argv[2]);

			addr |= 0x800000;
			sysprintf("Write [%X] = %X\n", addr, data);
			data = pokepeek(addr, data);
			sysprintf("Readback [%X] = %X\n", addr, data);
#endif
		} else if (0 == strcmp(argv[0], "b")) {
		}

		uart_puts("OK.");
	}

	return 0;
}
#endif
